Preparing OPD Teacher by Merging LoRA weights in verl-omni

Follow the official tutorial for DiffusionOPD Trainer. This post briefly describes how to merge LoRA weights into base model through Diffusers.

Table of Contents

We take Stable Diffusion 3.5 Medium as an example. We first run the FlowGRPO trainer for stable diffusion, and it will generate a checkpoints folder (something like checkpoints/flow_grpo/sd35_medium_ocr_lora).

1. Step 1. Export LoRA from FSDP Checkpoint

There’s already a tool export_fsdp_lora_adapter() in verl_omni/utils/fsdp_utils.py that supports this. It directly aggregates lora_* tensors from FSDP checkpoints, and outputs standard PEFT format (adapter_config.json + adapter_model.safetensors).

$ python3 -c "
from verl_omni.utils.fsdp_utils import export_fsdp_lora_adapter
print(export_fsdp_lora_adapter(
    'checkpoints/sd35_medium_ocr_distill/global_step_100/actor',
    output_dir='checkpoints/sd35_ocr_lora_adapter',
    base_model_name_or_path='stabilityai/stable-diffusion-3.5-medium', # or absolute path
))"

2. Step 2. Merging LoRA Weights and Save the New Model to Directory

I did meet some problems here1. The solution is that, we need to first edit the exported adapter_config.json in checkpoints/sd35_ocr_lora_adapter:

  1. set task_type key to null
  2. modify the term "0" of key target_modules to to_out.0

Then, we should merge the exported LoRA and the transformer part through PEFT.

  1. Copy the stablilityai/stable-diffusion-3.5-medium folder to checkpoints/sd35_ocr_merged_teacher
  2. Run the following Python script to load the transformer part with SD3Transformer2DModel and merge via PeftModel.from_pretrained then merge_and_unload.
import shutil, torch
from diffusers import SD3Transformer2DModel
from peft import PeftModel

base = "stabilityai/stable-diffusion-3.5-medium"
out = "checkpoints/sd35_ocr_merged_teacher"
lora = "checkpoints/sd35_ocr_lora_adapter"

tf = SD3Transformer2DModel.from_pretrained(
    base,
    subfolder="transformer",
    torch_dtype=torch.bfloat16
)
tf = PeftModel.from_pretrained(tf, lora)
              .merge_and_unload()

tf.save_pretrained(f"{out}/transformer")  # Only overwrite the transformer part

You may verify by running sha256sum ....../transformer/diffusion_pytorch_model.safetensors.


And we’re done! We can then pass TEACHER_PATH=/path/to/sd35_ocr_merged_teacher as argument to run Diffusion OPD with Stable Diffusion 3.5 Medium with single teacher.

Footnotes:

1

In fact, at first time, I followed a wrong approach and exported an unmerged SD3.5Medium. How did I find this? Because I found that the reward@1 remained a very low level (around 0.22), fluctuated, and did not improve at all; as well as the actor/distill_kl_loss remained \(0\) over time. LMAO

Date: 2026-09-18 Fri

Author: ArcaLunar